mod foundation;
mod graphics_types;
mod text;

use foundation::*;
use text::*;

use crate::{Fallback, Stretch, Style, Weight, XFontType};

pub struct XFont;
impl Default for XFont {
	fn default() -> Self {
		// let families = unsafe { CTFontManagerCopyAvailableFontFamilyNames() };
		// // let families = unsafe { CTFontManagerCopyAvailablePostScriptNames() };
		// let families = CFArray(CFValue::create_rule(families));

		// let len = families.len();
		// let mut datas = Vec::with_capacity(len as _);
		// for idx in 0..len {
		// 	if let Some(v) = families.get(idx) {
		// 		let family: CFStringRef = v as _;
		// 		let family = CFString(CFValue::get_rule(family));
		// 		datas.push(family.to_string());
		// 	}
		// }
		Self
	}
}
impl XFontType for XFont {
	fn fallback(&self, text: &str, name: &str, weight: Weight, stretch: Stretch, style: Style) -> Fallback {
		self.fallback_impl(text, name, weight, stretch, style).unwrap_or_default()
	}
	fn system() -> String {
		let font = CTFont::ui_for_language(kCTFontSystemFontType, 0.0, core::ptr::null_mut());
		font.family().unwrap_or("System Font".to_string())
	}
}
impl XFont {
	fn fallback_impl(&self, text: &str, name: &str, weight: Weight, stretch: Stretch, style: Style) -> Option<Fallback> {
		let symbolic = build_symbolic(weight.0, stretch.0, matches!(style, Style::Italic));
		let attributes = CFDictionary::from_pairs(&[
			(unsafe { kCTFontFamilyNameAttribute }, CFString::new(name).into()), //
			(
				unsafe { kCTFontTraitsAttribute },
				CFDictionary::from_pairs(&[
					(unsafe { kCTFontWeightTrait }, CFNumber::from(conv_weight(weight.0)).into()), //
					(unsafe { kCTFontWidthTrait }, CFNumber::from(conv_stretch(stretch.0)).into()),
					(unsafe { kCTFontSymbolicTrait }, CFNumber::from(symbolic as i32).into()),
				])
				.into(),
			),
		]);
		let font = CTFont::from_descriptor(&CTFontDescriptor::from_attributes(&attributes), 0.0);
		let font = if text.is_empty() {
			font
		} else {
			let text = CFString::new(text);
			font.for_string(text.as_ref(), CFRange::init(0, text.char_len()))
		};
		Some(Fallback {
			index: None,
			unique: font.unique_id(),
			file: font.font_path()?,
		})
	}
}

fn build_symbolic(weight: u16, stretch: u16, italic: bool) -> u32 {
	let mut symbolic = 0u32;
	if weight >= 700 {
		symbolic |= kCTFontBoldTrait;
	}
	if italic {
		symbolic |= kCTFontItalicTrait;
	}
	if stretch < 1000 {
		symbolic |= kCTFontCondensedTrait;
	} else if stretch > 1000 {
		symbolic |= kCTFontExpandedTrait;
	}
	symbolic
}
fn conv_weight(weight: u16) -> f64 {
	if weight < 150 {
		-0.80
	} else if weight < 250 {
		-0.60
	} else if weight < 350 {
		-0.40
	} else if weight < 450 {
		0.0
	} else if weight < 550 {
		0.23
	} else if weight < 650 {
		0.30
	} else if weight < 750 {
		0.40
	} else if weight < 850 {
		0.56
	} else if weight < 925 {
		0.62
	} else {
		1.00
	}
}
fn conv_stretch(stretch: u16) -> f64 {
	if stretch < (500 + 625) / 2 {
		-0.5
	} else if stretch < (625 + 750) / 2 {
		-0.37
	} else if stretch < (750 + 875) / 2 {
		-0.25
	} else if stretch < (875 + 1000) / 2 {
		-0.13
	} else if stretch < (1000 + 1125) / 2 {
		0.0
	} else if stretch < (1125 + 1250) / 2 {
		0.13
	} else if stretch < (1250 + 1375) / 2 {
		0.25
	} else if stretch < (1375 + 1500) / 2 {
		0.37
	} else {
		0.5
	}
}
